package code

import (
	"fmt"
	"github.com/astaxie/beego/orm"
	"io/ioutil"
	"regexp"
	"sort"
	"strconv"
	"strings"
)





//本地数据库权限自动生成
type LocalPermHelp struct {
	databasePerms []Perm //数据库权限数据
	fileParsePerms []Perm //从控制器文件中解析出的权限数据

	controllerDirFiles []string //控制器下文件
	exceptControllerFiles []string //设置不需要分析的文件
}


//本地数据库权限自动生成
func NewLocalPermHelp(exceptControllerFiles ...string) *LocalPermHelp {
	this:=&LocalPermHelp{
		exceptControllerFiles:exceptControllerFiles,
	}

	//载入控制器下所有文件
	this.loadControllerFiles().setFileParsePerms()


	//载入数据库中权限数据
	this.loadDatabasePerms()


	return this
}

//载入controller下所有控制器文件
func (this *LocalPermHelp)loadControllerFiles() *LocalPermHelp {

	files, _ := ioutil.ReadDir(ControllerDir)

	for _, f := range files {
		if strings.Contains(f.Name(), "_test.go") ||!strings.Contains(f.Name(),".go") {
			continue
		}

		//不需要分析的文件
		if inArray(f.Name(),this.exceptControllerFiles) {
			continue
		}

		this.controllerDirFiles=append(this.controllerDirFiles,f.Name())
	}
	return this
}

//载入数据库中权限数据
func (this *LocalPermHelp)loadDatabasePerms() *LocalPermHelp {

	if _, err := orm.NewOrm().QueryTable(PermTableName).Limit(-1).All(&this.databasePerms); err != nil {
		fmt.Println("LocalPermHelp.getPerm 错误：",err)
	}

	return this
}


//通过permUrl 获取数据库中权限
func (this *LocalPermHelp) getPerm(permUrl string ,perms []Perm) *Perm {

	for k:=range perms {
		if perms[k].PermUrl==permUrl {
			return &perms[k]
		}
	}

	return nil
}

//计算控制文件中的权限
func (this *LocalPermHelp)setFileParsePerms()  {
	for _,file:=range this.controllerDirFiles {
		this.fileParsePerms=append(this.fileParsePerms,parseControllerFiles(ControllerDir+file)...)
	}

}

//从注释中解析出方法名称和PermTreeId  测试数据:comment="获取登录用户信息{perm:22}"
func parseMethodNameAndPermTreeId(comment string)(name string,permTreeId uint32)  {
	if !strings.Contains(comment, "{perm") {
		return
	}

	comment=strings.TrimRight(comment,"}")
	commentArr:=strings.Split(comment,"{perm:")

	if len(commentArr)!= 2 {
		return
	}

	name=commentArr[0]

	id,_:=strconv.Atoi(commentArr[1])
	permTreeId=uint32(id)


	return
}

//从注释中解析出permUrl 测试数据: controller="UserController" action="AuthUser"
func parsePermUrl(controller string ,action string)(permUrl string)  {
	if controller=="" || action== "" {
		return
	}
	controller=strings.Replace(controller,"Controller","",1)


	permUrl=strings.ToLower(controller)+"/"+strings.ToLower(action)
	return
}

//从文件获取带有权限的方法
func parseControllerFiles(file string) (perms []Perm) {

	content,err:=ioutil.ReadFile(file)
	if err != nil {
		fmt.Println(err)
		return
	}

	re:=regexp.MustCompile(fmt.Sprintf(`//([^\n]+)\nfunc \(this \*([A-Z][A-Z0-9a-z]*Controller)\) ([A-Z][A-Z0-9a-z]*)\(\) \{`))
	res:=re.FindAllStringSubmatch(string(content),-1)

	for _,v:=range res {
		if len(v)!= 4 {
			continue
		}
		name,permTreeId:= parseMethodNameAndPermTreeId(v[1])
		//必须要permTreeId 才进行解析
		if permTreeId== 0 {
			continue
		}

		permUrl:= parsePermUrl(v[2],v[3])

		perms=append(perms,Perm{
			Name:name,
			PermTreeId:permTreeId,
			PermUrl:permUrl,
		})

	}

	return
}


func inArray(s string ,ss []string) bool {
	for _,v:=range ss {
		if s== v {
			return true
		}
	}

	return false
}


//制造sql
func (this *LocalPermHelp)makeSql() string  {
	var (
		databasePerm *Perm
		contentStr string

	)

	//先对分析出的权限数据排序
	sort.Slice(this.fileParsePerms, func(i, j int) bool {
		return this.fileParsePerms[i].PermUrl<this.fileParsePerms[j].PermUrl
	})


	for _,filePerm:=range this.fileParsePerms {
		if databasePerm=this.getPerm(filePerm.PermUrl,this.databasePerms);databasePerm!= nil {

			//不需要更新
			if filePerm.Name==databasePerm.Name &&filePerm.PermTreeId==databasePerm.PermTreeId {
				continue
			}

			//数据库已经存在的数据
			contentStr+=fmt.Sprintf(UpdatePermSql,filePerm.Name,filePerm.PermTreeId,databasePerm.Id)


		}else{
			//新增的数据
			contentStr+=fmt.Sprintf(InsertPermSql,filePerm.Name,filePerm.PermUrl,filePerm.PermTreeId)

		}

	}

	return contentStr


}

//生成sql文件
func (this *LocalPermHelp)CreateSql(output string) error {

	contentStr:=this.makeSql()

	//输出到文件
	return ioutil.WriteFile(output,[]byte(contentStr),0644)
}

//直接更新本地数据
func (this *LocalPermHelp)UpdateTable() error {

	contentStr:=this.makeSql()

	contentArr:=strings.Split(contentStr,";")

	o := orm.NewOrm()
	for _,line:=range contentArr {
		line:=strings.Trim(line,"\n")
		if line== "" {
			continue
		}
		if _, err := o.Raw(line).Exec();err!= nil {
			return err
		}

	}

	return nil
}


//打印数据库中多余的权限
func (this *LocalPermHelp)PrintDiff()  {
	for _,v:=range this.databasePerms {
		if perm:=this.getPerm(v.PermUrl,this.fileParsePerms);perm== nil {
			fmt.Printf("据库中多余的权限:%+v\n",v)
		}
	}
}



